import { useCallback, useEffect, useRef, useState } from 'react'
import Recorder from 'recorder-js'

export const createRecorder = (
  callback: (result: string, error: Error | null) => void,
  onSTTStart?: () => void,
  onSTTEnd?: () => void,
): RecorderController => {
  let gBlob: Blob | null = null
  let recorder: Recorder | null = null

  async function startRecording() {
    const audioContext = new (window.AudioContext
      || window.webkitAudioContext)()
    recorder = new Recorder(audioContext, {
      // An array of 255 Numbers
      // You can use this to visualize the audio stream
      // If you use react, check out react-wave-stream
      // onAnalysed: (data) => console.log(data),
    })
    await navigator.mediaDevices
      .getUserMedia({ audio: true })
      .then(stream => recorder!.init(stream))
      .catch(err => console.log('获取音频流失败...', err))
    return recorder.start()
  }

  async function stopRecording() {
    return recorder!.stop().then(({
      blob,
    }) => {
      gBlob = blob
      const stream: MediaStream = recorder!.stream
      stream.getTracks().forEach((track) => {
        track.stop()
      })
      // buffer is an AudioBuffer
    }).catch((e) => {
      console.error('Voice Error:', e)
      callback?.('', e)
    })
  }

  const sid = '10086'

  async function download() {
    if (!gBlob)
      return false
    onSTTStart?.()
    // Recorder.download(gBlob, "my-audio-file"); // downloads a .wav file
    await fetch(`${window.__gVbaseUrl}/api/stt?sid=${sid}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/octet-stream',
      },
      body: await getArrayBufferFromBlob(gBlob),
    })
      .then(response => response.json())
      .then((data) => {
        console.log('Server response:', data)
        const rr = data as VoiceSTTResponse
        if (rr.code === 1000) {
          callback?.(rr.result.map(v => v.text).join(''), null)
          return
        }
        const e: any = new Error(rr.message)
        e.requestId = rr.reqid
        e.code = rr.code
        e.response = rr
        callback?.('', e)
      })
      .catch((error) => {
        console.error('Error:', error)
      }).finally(() => {
        onSTTEnd?.()
      })
    return true
  }

  function getArrayBufferFromBlob(blob: Blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader()
      reader.onload = () => {
        // 结果存储在 result 属性中
        resolve(reader.result)
      }
      reader.onerror = (error) => {
        reject(error)
      }
      // 开始读取 Blob 数据
      reader.readAsArrayBuffer(blob)
    })
  }

  return {
    startRecording,
    stopRecording,
    download,
  }
}

export type RecorderController = {
  startRecording: () => Promise<MediaStream | undefined>
  stopRecording: () => Promise<void>
  download: () => Promise<boolean>
}

export type Result = {
  text: string
  confidence: number
}

export type VoiceSTTResponse = {
  reqid: string
  code: number
  message: string
  sequence: number
  result: Result[]
}

export type UseXFVoiceOptions = {
  onRecordEnd?: (text: string) => void
  onError?: (error: Error) => void
  onSTTStart?: () => void
  onSTTEnd?: () => void
}
// const RECORD_STATE = {
//   INITIAL: 0,
//   WAITING_START: 3,
//   START: 1,
//   RECORDING: 2,
//   WAITING_STOP: 4,
//   STOP: 5,
// }
export const useHSVoice = (options?: UseXFVoiceOptions) => {
  const {
    onRecordEnd,
    onSTTStart,
    onSTTEnd,
    // onError,
  }
    = { ...options }
  const recorderRef = useRef<RecorderController | null>(null)
  const [recordState, setRecordState] = useState(0)
  const [recText, setRecText] = useState('')
  const recordStateRef = useRef(recordState)

  useEffect(() => {
    recorderRef.current = createRecorder((msg, error) => {
      if (error) {
        // onError?.(error)
        console.error('Xz-chat: ', error)
        onRecordEnd?.('')
        return
      }
      setRecText(msg)
      onRecordEnd?.(msg)
    }, onSTTStart, onSTTEnd)
  }, [])
  const start = async () => {
    if (recordStateRef.current !== 0)
      return

    setRecordState(1)
    recordStateRef.current = 1
    await recorderRef.current!.startRecording()
  }
  const stop = async () => {
    if (recordStateRef.current !== 1)
      return

    setRecordState(0)
    recordStateRef.current = 0
    await recorderRef.current!.stopRecording()
    await recorderRef.current!.download()
  }
  const triggerRecord = useCallback(() => {
    if (recordStateRef.current === 1) {
      stop()
      return
    }
    start()
  }, [])
  return {
    triggerRecord,
    recStatus: recordState,
    recText,
    recSec: 0,
    isRecording: () => recordState === 1,
  }
}
